home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD Exchange
/
CD Exchange - Volume 1.iso
/
utils
/
misc
/
ace
/
ace-2.0.lha
/
DOCS.LHA
/
ace.doc
< prev
next >
Wrap
Text File
|
1994-01-19
|
87KB
|
2,419 lines
--------
ACE v2.0
--------
CONTENTS LINE PAGE
-------- ---- ----
(i) Introduction
What is ACE?..................................80 2
Who is it for?...............................165 3
------------------------------------------------
(ii) Getting started
Installation.................................213 4
Using the compiler...........................287 5
Compiler options.............................334 6
Running ACE programs.........................402 7
About the example programs...................413 7
------------------------------------------------
(iii) A hitch-hiker's guide to ACE
General comments.............................446 8
The preprocessor and #include files..........477 8
Data types, expressions, constants...........520 9
Precedence of operators......................602 10
Identifiers..................................634 11
Indirection operators........................752 13
Files........................................788 13
Command line and Workbench arguments.........885 15
Subprograms..................................934 16
Structures..................................1163 19
Shared library function calls...............1266 21
Machine code calls..........................1405 23
External references.........................1477 24
Windows.....................................1521 25
Screens.....................................1543 25
Gadgets.....................................1575 26
Menus.......................................1695 28
Requesters..................................1719 28
Turtle Graphics.............................1744 29
Sound.......................................1849 30
Event trapping..............................1946 32
Error handling..............................2044 33
- page 1 -
------------------------------------------------
(iv) Stop bits
Notes for assembly programmers..............2078 34
Limitations.................................2123 35
Known bugs..................................2165 35
Future versions.............................2182 36
A note to PD libraries and reviewers........2221 36
Disclaimer..................................2242 37
References..................................2249 37
Final word..................................2291 37
================================================
What is ACE?
------------
AmigaBASIC Compiler with Extras?
A Creative Environment?
A Compiler for Everyone?
A Cool Enterprise?
Automatic Computing Engine (ala Alan Turing)?
Dr Who's last companion?
Okay, seriously...
ACE is a freely distributable, recursive descent, peephole-optimising Amiga
BASIC compiler which produces A68K-compatible assembly source code. ACE runs
under Wb 1.3, 2.x and up, as do the executables it produces. ACE will run
in 512K, but more than this is required for programs beyond about 250 lines.
ACE supports a large subset of AmigaBASIC. It also provides a variety of
commands, functions and features not found in AmigaBASIC.
In most cases, ACE programs produce results which are similar or identical
to programs written in AmigaBASIC.
Any differences between the two are discussed in this document and the
command and function reference.
The following files constitute a complete ACE package:
executables
-----------
bas - A shell script which automates
the production of ACE executables.
app - A simple preprocessor.
ace - The BASIC compiler.
a68k - Charlie Gibbs' 68000 assembler.
blink - The Software Distillery's linker.
muchmore - The file viewer by Fridtjof Siebert
(version which works under Wb 1.3).
documents
---------
ace.doc - The document you are reading which describes ACE.
ref.doc - A command and function reference for ACE.
history - A history of ACE's development.
- page 2 -
a68k.doc - Documentation for the assembler.
blink.doc - Documentation for the linker.
scanned libraries
-----------------
ami.lib - A freely distributable version of amiga.lib.
startup.lib - A library of routines needed at the start and
end of an ACE program run.
db.lib - A library of assorted routines used by ACE
programs.
other stuff
-----------
includes - Some useful ACE include files.
icons - ACE tool and document icons.
utils - Miscellaneous utilities.
examples - Example programs which demonstrate many of ACE's
capabilities.
AIDE - A simple graphical front-end for ACE with "make"
capabilities.
With one exception (see the discussion of file requesters under Wb 1.3 in
the "Requesters" section below) ACE programs do not require any special
run-time shared libraries, so the executables which the compiler produces
(via the assembler and linker) are completely portable, requiring only the
standard Amiga shared libraries in your LIBS: directory. The three ".lib"
files mentioned above are scanned libraries and code from these is included
at link time.
ACE is written in C (Sozobon's ZC v1.01), while db.lib and startup.lib are
written in assembler (~60%) and C.
A68K and Blink are used to assemble and link the code produced by ACE.
The MicroEmacs (v1.3 & v2.1) editor has been used throughout every stage
of ACE's development. It works for me.
The complete ACE package may be freely distributed.
Who is it for?
--------------
ACE is intended for anyone who already knows BASIC and wants one or more of
the following:
- Faster program execution.
- Independence from the BASIC interpreter, ie: standalone
programs which are runnable from the CLI/Shell and Workbench.
- Extra commands, functions and features: turtle graphics,
command-line (and Workbench) arguments, recursion, SUBs with
return values, external references, named constants, structures,
include files, better WAVE command, gadgets, requesters, etc.
Maybe you don't wish to learn another high-level language, or perhaps you
already use C or assembler but prefer to use BASIC for some tasks while
still having the power of a compiled language.
ACE is a general purpose language so in theory at least, it can be applied
- page 3 -
to any programming task you like. In practice however, I find ACE to be most
useful for writing small to medium sized programs where speed is important
but so is ease of programming.
ACE is also a useful prototyping language. It allows you to get something
up and running quickly to test an idea. You may later decide to re-code in
C or assembler, or you may just add some polish to the existing ACE program.
The latter is becoming more feasible as ACE matures.
I have written the following kinds of programs with ACE:
- Shell utilities, eg: basic calculator (ACE:prgs/ShellUtils/bc.b).
- Fractal/Chaos programs.
- Neural networks.
- An 8SVX sound sample player.
- Astronomy programs, eg: galaxy collision simulator,
Jovian satellite motion simulator, Messier object database.
- An Integrated Development Environment (AIDE - see separate
archive).
- A program which matches section headings to line and page
numbers to create the document you are now reading from an
unformatted version of it.
Installation
------------
You will need to open a shell to install ACE. Installation consists of:
- Extracting the four archives found in the single supplied ACE
archive.
- Adding a few commands to your s:user-startup (Wb 2.x/3.0) or
s:startup-sequence (Wb 1.3) file.
The ACE distribution archive contains:
- MAIN.lha (main ACE files with a few examples)
- DOCS.lha (documentation for ACE,A68K and Blink)
- PRGS.lha (more example programs)
- AIDE.lha (a graphical front-end for ACE)
All four will fit onto a single floppy disk in their compressed form.
You must now extract the files from each archive with:
lha x <archive>
If you have a hard disk, just extract all four archives into a directory
created for ACE (eg: sys:ACE). Extract MAIN.lha first. This will set up the
directory structure and main ACE files.
The extraction of MAIN.lha also creates three subdirectories for the last
three archives. So, finally, extract DOCS.lha, PRGS.lha and AIDE.lha into
the docs, prgs and AIDE subdirectories.
If you are using a floppy-only system, extract MAIN.lha onto one disk and
the last three archives onto other disks to suit yourself. The disk on which
the main ACE files reside is the volume to which the shell commands discussed
- page 4 -
below refer.
You now have all the files you require to use ACE.
Next, add the following lines to your user-startup or startup-sequence
script:
assign ACE: <volume or directory>
path ACE:bin add
where <volume or directory> is the name of the disk or directory where
the main ACE files now reside (eg: assign ACE: sys:ACE).
In addition, you need to add three more statements to your startup-sequence
or user-startup script:
assign ACElib: ACE:lib ; bas finds scanned libraries here.
assign ACEbmaps: ACE:bmaps ; ace looks here for .bmap files.
assign ACEinclude: ACE:include ; app uses this for include files.
You may also have to set the "s" bit on ACE:bin/bas if it was lost during
the unarchiving process. Do this with the following command:
protect ACE:bin/bas +s add
If you're not sure, do it anyway. This simply lets you run the bas script
without having to use the "execute" command.
Now reboot your Amiga to let the above path and assign commands take effect.
That's it!
** Note: As an alternative to these startup script modifications, you can
use the "ACEsetup" script to be found in MAIN.lha. Read the comments at
the top of that script for usage details.
Using the compiler
------------------
Starting with ACE v2.0 there are two ways to use the compiler:
- From the shell/CLI.
- Via an Integrated Development Environment: AIDE.
Whichever environment you choose to work with ACE in, read on.
ACE expects all BASIC source files to have a ".b" extension.
If you have a program called foo.b, you would invoke the compiler thus:
ace foo (or ace foo.b)
This would produce foo.s, an A68K-compatible assembly source (text) file.
If you wanted to preprocess, compile, assemble and link foo.b, you'd type:
bas foo
- page 5 -
which would yield foo (the executable).
The bas script sets the stack to 40000 bytes. Before running ACE by itself,
you will need to set this. A minimum stack size seems to be around 5000
for many ACE compilations, but I recommend 40000 to be safe.
If your Amiga GURUs or hangs during a compile or produces garbage in the
shell, you can be confident that the stack is too small.
You can either create a BASIC source file using an editor or in the AmigaBASIC
environment. If you want to compile a program developed with the interpreter,
just save the program in ASCII format thus:
save "foo.b",a
ACE will only compile ASCII source files, not AmigaBASIC's compressed format.
For those who don't have access to the AmigaBASIC interpreter but who wish
to convert old AmigaBASIC programs not saved in ASCII format, see the
ACE:utils directory for a utility called ab2ascii written by Stefan Reisner.
Compiler options
----------------
The full command line syntax for ACE is:
ace [-bcEilOw] <sourcefile>[.b]
which indicates that there are currently seven optional compiler switches.
The switches can appear in any combination (eg: -bO, -clb, -O, -ObE) but
*are* case sensitive (so -b does not equal -B).
The "b" switch tells the compiler to include code to check for ctrl-c breaks
by the user. The inclusion of this code can result in noticeably larger
assembly source files, but execution speed doesn't seem to suffer appreciably.
When a ctrl-c is detected, the program will clean up and exit but user-defined
windows and screens will remain open. The use of ON BREAK can get around this
by allowing for user-controlled clean up (see the "Event Trapping" section
below).
The "c" switch includes each line of ACE source code as a comment in the
final assembly source file. This was requested as a debugging aid. Warning:
the presence of such comments interferes with peephole optimisation. Also be
aware that ACE sometimes includes extra code apart from that which you would
expect purely on the basis of the source code.
The "E" switch creates a file in the current directory called ace.err which
contains all error messages generated during a compliation. Error messages
are still displayed to the screen during compilation however.
The "i" switch tells ACE to make an icon for the executable resulting from
the compilation. The file "ACE:icons/exe.info" must exist as it is used as
the source file for the icon. This allows you to use an icon of your own if
you so wish.
The "l" switch causes the compiler to display each line of ACE source code
as it is being compiled.
- page 6 -
The "O" switch causes the assembly source code produced by ACE to be optimised.
At present, simple peephole optimisation is carried out. Assembly code size
reductions of around 5% to 10% are usual. Speed improvements vary, depending
upon the program, however I recommend the use of the -O switch for all programs
where speed is the least bit important. As time goes by, ACE's optimiser will
be improved.
The "w" switch tells ACE to include checks for window close-gadget clicks.
ACE checks all open windows and upon detecting a close-gadget click, the
clicked window is closed and the program exits. However, any other open
windows or screens will not be closed. The use of ON WINDOW can get around
this by allowing for a user-defined clean-up subroutine (see the "Event
Trapping" section below).
The syntax for the bas script is:
bas [-bcEilOw] <sourcefile> [<objectfile>]
where <sourcefile> is the program to be compiled (without the .b extension)
and <objectfile> is a C or assembly module which has been (compiled and)
assembled to produce an object file.
The <objectfile> is linked with the output of ACE+A68K along with db.lib,
startup.lib and ami.lib. This is necessary when an external function or
variable in <objectfile> is referenced by an ACE program. For more about
external functions, see the section "External references" below.
Running ACE programs
--------------------
ACE programs can be run from either a shell/CLI or Workbench. In the latter
case a tool icon must be created for the executable. One has been provided
with the archive in the icons directory (exe.info). Refer to the "i" switch
in "Compiler options" above re: automatic icon creation by the compiler.
As far as I can tell, ACE programs are pure and so can be made resident once
the pure bit is set with the protect command. I've had no problems so far.
About the example programs
--------------------------
I have written a number of programs which illustrate most of the features
of ACE up to this point and you should find these with the distribution.
Several programs are related to chaos theory and fractals - a pet interest
of mine (and of thousands of other programmers :). The remainder are an
assorted bunch which demonstrate ACE's capabilities.
The information for the chaos/fractal programs came from a wide variety of
sources. The algorithms for henon.b and lorenz.b came from "Dynamical systems
and fractals: computer graphics experiments in Pascal" by Becker & Dorfler,
1990.
Some programs are optimised at the source level and some are not. You'll
find that using integer variables can often result in quite dramatic
improvements in program execution speed (eg: try replacing op% with op and
k% with k in ifs.b and you'll see what I mean).
There are several examples which demonstrate the use of recursive
- page 7 -
subprograms in ACE (eg: fact.b).
Another area of interest for me is neural networks and you'll find a
program called hopnet.b, which shows graphically how a simple Hopfield
network changes under various conditions.
Other programs include a talking clock (tclock.b), a sound sample player
(play.b) and a command-line calculator (bc.b).
General comments
----------------
I made a decision very early on in the project to allow standard I/O (for
shell/CLI). All other windows are (as of ACE v2.0) Intuition windows.
The execution speed of most programs (especially with graphics, eg: ifs.b)
is, as you might expect, *fast* compared to interpreted AmigaBASIC.
No error messages are given at run-time (but file and serial I/O errors are
reported by the ERR function), nor is there any stack overflow or array
bounds checking.
Labels are supported and can be used with GOSUB and GOTO. Line numbers are
supported, but are only necessary for old BASIC programs. Also, ACE's rich
assortment of control constructs makes the use of GOTO largely redundant.
Available control constructs are: WHILE..WEND, REPEAT..UNTIL, IF..THEN..ELSE,
IF..THEN..ELSE..END IF, CASE..END CASE, ON..GOTO, ON..GOSUB, SUB..END SUB and
GOSUB..RETURN.
Apart from single line comments with REM and ' ACE allows block comments with
{ and }. For example:
{ comments can span more than
one line like this }
Multi-statements are also supported by ACE, eg:
x$="hello":y$="there":say translate$(x$+" "+y$)
The preprocessor and #include files
------------------------------------
The ACE preprocessor: APP, is modest when compared to the C preprocessor.
Its main function thus far is for the inclusion of files with the #include
directive. As in C, #include "filename" looks for the file as specified,
while #include <filename> looks for the file in a local include directory
(see "Installation"). A file will only ever be included once.
The #include directive can also be used in included files, but since file
inclusion is recursive, watch your stack size (40000 bytes is plenty).
APP also handles single-line comments (text following a "'") and block
comments (starting with "{" and ending with "}"). This is partly to allow
#include commands to be commented out and also to make less work for the
compiler. However, the compiler does still handle comments in case the pre-
processor isn't invoked. APP does not handle REM since this is a BASIC
statement.
- page 8 -
The syntax for APP is:
app <source> <dest>
The bas script uses APP by first preprocessing an ACE source file to the
RAM:T directory.
Use of #include has the effect of adding lines to the preprocessed ACE
source, which has an impact upon the physical location of lines from the
main ACE file when transfered to the destination file.
ACE include files have two purposes. As in C they can be used to include
constants and structure definitions. Second, as with files like WBarg.h,
ACE include files may contain subprogram definitions and although this is
quite possible in C, it seems to be less often done under the guise of .h
files. I have arbitrarily chosen to append all include files with .h but
there is no reason why this need be so. Purists will probably be aghast.
APP will be improved as time goes by.
Data types, expressions and constants
-------------------------------------
The following fundamental data types are currently supported:
- signed short integers (2 bytes = 16 bits)
- signed long integers (4 bytes = 32 bits)
- single-precision: Motorola fast floating point (4 bytes = 32 bits)
- strings (default to 1024 bytes including ASCII 0 end-of-string)
Exponential and fixed-point formats are recognised by all ACE functions
and in program text for single-precision numbers.
Expression parsing is the same as for AmigaBASIC, as is the precedence of
operators. Evaluation of _all_ expressions proceeds from left to right.
This includes exponentiation, so 2^3^2 will be evaluated as (2^3)^2.
In addition, due to the higher precedence of exponentiation over unary
negation and the way ACE's recursive descent parser works, 4^(-2) is okay,
but 4^-2 isn't.
ACE supports full 32-bit and single-precision floating point math:
addition, subtraction, multiplication:
- 16-bit integer
- 32-bit integer
- single-precision
division & modulo arithmetic:
- 32-bit integer
- single-precision
Increment and decrement operators are provided in ACE in the following form:
++<variable> OR --<variable>
- page 9 -
The value of the simple or external variable is incremented or decremented
by 1.
Notice that ++ and -- are pre-increment & pre-decrement operators ONLY. Those
familiar with C will recognise these operators and their utility. In terms of
efficiency: ++x is better than x=x+1.
Unlike interpreted AmigaBASIC, hexadecimal and octal constants can be either
short or long values. This makes for nicer addressing with PEEK & POKE.
Trailing characters (%&!#) after constants cause coercion from one numeric
data type to another, as in AmigaBASIC, eg:
Delay(50&) '..50 is coerced from short to long integer
x=12.5*65! '..65 is coerced from short integer to single-precision
As in AmigaBASIC expression evaluation, all operands in an expression are
converted to the data type of the most precise operand. Logical operators
(AND,EQV,IMP,NOT,OR,XOR) convert their operands to integer values as does
the integer division operator "\". Relational operators (= <> > < >= <=)
yield long integer results.
ACE's boolean values are as follows: 0=false, N=true where N is any
non-zero long integer. Note that relational operations give -1 for true
(since: NOT -1 = 0).
ACE allows you to define named global signed numeric constants with the
CONST directive (see command and function reference).
Strings have a default length of 1K instead of the usual 32K, since ACE
programs reserve memory for each string immediately at run-time which could
result in quite memory hungry executables if strings were too large. It is
possible however, to define strings which are longer or shorter than 1K (see
STRING command).
ACE strings are NULL terminated, ie: the last character is an ASCII 0, as
in the C programming language.
A string literal without a final '"' will be truncated at the end of the
line.
Precedence of operators
-----------------------
ACE follows AmigaBASIC in operator precedence, with the addition of structure
dereferencing and indirection operators.
operators
---------
1. Structure Member Dereferencing, ->
Parentheses and Address Operator () @
2. Indirection Operators *% *& *!
3. Exponentiation ^
4. Unary Negation -
5. Multiplication and Floating-Point Division * /
6. Integer Division \
7. Modulo Arithmetic MOD
8. Addition and Subtraction + -
- page 10 -
9. Relational Operators = < > <= >= <>
10. NOT
11. AND
12. OR and XOR
13. EQV
14. IMP
The use of parentheses in an expression forces the enclosed term to be
evaluated before adjacent terms. Expression evaluation always proceeds
from left to right in ACE and AmigaBASIC.
Identifiers
-----------
As in AmigaBASIC an identifier can consist of a combination of letters,
numbers and periods (".") up to a maximum length of 40 characters.
In ACE the underline ("_") character is also legal. An ACE identifier must
start with either a letter or an underline character.
An identifier can be used to represent the following:
- labels
- arrays
- variables
- structures
- parameters
- subprograms
- defined functions
- named constants
- shared library functions
- external functions or variables
Labels are global in ACE, so a main program label and a SUB label cannot
have the same name.
Identifiers can have a qualifier character (%&$!#) appended in order to
indicate data type, where:
% = short integer
& = long integer
! = single-precision
# = double-precision -> not supported yet
$ = string
Examples of valid identifiers are:
x3
num&
_putchar
play.sound
An identifer with no qualifier has a default type of single-precision. The
DEFxxx compiler directives (see command and function reference) have the
same effect as the qualifier characters except they affect all identifiers
starting with a certain letter. Qualifier characters have higher precedence
than DEFxxx directives.
For shared library functions and external references, a qualifier is used
- page 11 -
merely to declare data type. So for example, an external function might be
declared thus:
external function RangeRand%
but can later be referred to as RangeRand.
The declaration of external functions/variables and shared library functions
is global no matter where the declaration occurs.
Defined constants are unaffected by qualifier characters. The _value_ of a
defined constant determines its type. Thus CONST x&=1.2 is a single-precision
- NOT a long integer - constant. Needless to say therefore, it is unwise to
use qualifier characters for named constants.
The declaration of constants (with CONST) is always global whether the
declaration takes place in the main program or a subprogram.
Structure variables hold a long integer value (address), so trailing
characters have no effect.
Structure type definitions are global, but structure variable declarations
are local.
ACE allows for *optional* variable declarations with the SHORTINT, LONGINT,
ADDRESS, SINGLE and STRING directives. Such declarations are useful in that:
(i) They ensure that a variable has a NULL or zero
value.
(ii) They prevent dangerous errors which result from
the misspelling of variable names.
(iii) Most languages have them and they serve to
document variable usage explicitly.
(iv) They provide a "cleaner" way of establishing a
variable which is to be shared by a subprogram.
My feeling on the matter of variable declarations is that in a small program
they probably aren't necessary so long as you are careful, but in a large
program all major variables should be declared for safety.
Variable declarations override the DEFxxx compiler directives and qualifier
characters and are local to the current level (main program or subprogram).
Summary of identifier properties:
+---------------+---------------+------------------+
| Identifier | Local/Global | Affected by %&!$ |
+---------------+---------------+------------------+
| ARRAY | LOCAL | YES |
| SIMPLE VAR | LOCAL | YES |
| STRUCTURE VAR | LOCAL | NO |
| PARAMETER | LOCAL | YES |
| STRUCTURE DEF | GLOBAL | NO |
| LABEL | GLOBAL | NO |
| NAMED CONST | GLOBAL | NO |
| SUBPROGRAM | GLOBAL | YES |
- page 12 -
| DEF FN | GLOBAL | YES |
| LIBRARY FUNC | GLOBAL | YES (declaration)|
| EXT VAR/FUNC | GLOBAL | YES (declaration)|
+---------------+---------------+------------------+
Indirection operators
---------------------
ACE has four indirection operators: @,*%,*&, and *!. These are _similar_
to pointers in C.
@<object> - returns the absolute address of a data object.
- note that this is identical to VARPTR(<object>).
*%<address> - peeks or pokes a short value at the specified address.
*&<address> - peeks or pokes a long value at the specified address.
*!<address> - peeks or pokes a single value at the specified address.
The indirection operators can therefore be used in a statement (poke) and/or
as part of an expression (peek), for example:
address x
y=23.25
x=@y
*!x := *!x + 2
print y
will print a value of 25.25.
There are two things to notice here. First, the pointers are to addresses,
not necessarily connected to variables. It would be quite legal to allocate
an area of memory and then dereference it with these operators.
Second, when assigning a value to a dereferenced memory location as in the
above example, the ":=" symbol must be used, simply because of the way the
parser processes statements. Pascal programmers will recognise this as
the assignment operator.
See also the section below for information about how to use these operators
to implement variable parameters (call-by-reference) for simple variables in
ACE subprograms.
Files
-----
AmigaBASIC sequential files are supported and random files are on the list
of things to do.
The commands and functions for manipulating sequential files in ACE are:
commands:
- OPEN
- CLOSE
- PRINT#
- WRITE#
- INPUT#
- LINE INPUT#
functions:
- INPUT$
- page 13 -
- EOF
- LOF
- HANDLE *
* not found in AmigaBASIC
Note that for any command which is immediately followed by a # there should
be at least one space between the keyword and the #, even though I may refer
to such commands as <name># in the text of this document and in ref.doc.
See the command and function reference for details of each of these.
When WRITE# is used, the result is identical to AmigaBASIC. For example:
X=12 : Y=-3.2 : Z$="fun eh?"
OPEN "O",#1,"stuff"
WRITE #1,X,Y,Z$
CLOSE #1
results in a one-line file of the following format:
12,-3.2,"fun eh?"
On the other hand, if the following is used instead:
PRINT #1,X,Y,Z$
the file format will be:
12 -3.2 fun eh?
while if semicolons are used:
PRINT #1,X;Y;Z$
the file format becomes:
12 -3.2 fun eh?
INPUT# (eg: INPUT #1,X,Y,Z$) can be used to read values from a file in any
of the above formats, but bear in mind that strings that are not delimited
by quotes, but contain spaces or tabs will be seen as more than one string
by INPUT#. So, in the example formats above, while
"fun eh?"
is one string,
fun eh?
is two strings as far as INPUT# is concerned.
The formats of sequential files in ACE and AmigaBASIC are now very nearly
identical, the only differences being in ACE tabs (produced by comma
delimiters in PRINT -- see PRINT in ref.doc) and the number of decimal
places written for single-precision values (usually more in ACE).
If you find ACE file I/O too slow, you may want to use the dos.library
functions (eg: xRead, xWrite). For this reason, I have included the HANDLE
- page 14 -
function which returns the AmigaDOS handle of a file opened with ACE's OPEN
command. You may also wish to use the ami.lib buffered file I/O functions
which also require this handle. If HANDLE returns 0, the file doesn't exist.
See prgs/IO/print.b for an example of opening a sequential file to a printer.
Although SER: may be opened as a sequential file, it is not possible to
specify parameters for the serial port (baud rate etc) by this method as
is possible in AmigaBASIC.
Instead, ACE provides a set of special serial I/O commands. See ref.doc for
details of SERIAL OPEN/CLOSE etc, and prgs/IO/aterm.b for a simple terminal
program.
Command line and Workbench arguments
------------------------------------
When called from a Shell or CLI, an ACE program may have arguments, eg:
tree 30
Arguments can be accessed by two ACE functions:
ARGCOUNT and ARG$(n)
The former returns a short integer value indicating the number of arguments
for the current program, while the latter returns the nth argument as a string
where n ranges from 0 to argcount. The zeroth argument (ie: ARG$(0)) is the
name of the program.
Workbench arguments are currently supported by ACE in the form of four
functions in the include file WBarg.h: WBargcount, WBarg$(n), WBargPath$(n)
and WBargLock&(n).
The first three are the most useful. The fourth is mainly for use by
WBargPath$.
WBargcount returns the number of arguments passed to a program as icons.
As with ARG$(0), the zeroth Workbench argument is the name of the program.
To pass arguments to a program via Workbench one of the shift keys is held
down while the icons which represent the arguments to be passed are activated.
While still depressing the shift key, the application icon is double clicked.
An alternative method of passing arguments is to change the default tool of a
project icon (eg: document) with the Info option from Workbench.
When this project icon is double clicked, the default tool will be loaded.
In this case, if the source code of the default tool (the program) had a line
such as:
x$ = WBarg$(1)
x$ would contain the name of the project file.
WBargPath$(n) is used to find the full path of the file name and includes
trailing ":" and "/" characters.
See the include file WBarg.h for further descriptions of each function.
- page 15 -
Subprograms
-----------
Subprograms are supported by ACE, but differ from AmigaBASIC subprograms in
a number of ways. Namely, ACE subprograms:
- Are non-static,
- Allow recursion,
- Can be assigned return values.
By way of explanation, being non-static means that once a subprogram has
finished execution, its local variables and parameters cease to exist so
far as the rest of the program is concerned.
Recursive subprograms are an important feature of modern general programming
languages. For several examples of the use of recursion, see the included
programs (eg: fact.b, hanoi.b, tree.b).
A word of warning about recursion: it can be stack hungry, so it's a good
idea to set your stack to 20000 or so, just to be safe, although in most
cases, this will be a lot more than you need. From Workbench, simply change
the tool's stack size with Info, or with the STACK command in a shell.
As with AmigaBASIC, ACE subprogram declarations cannot be nested.
The syntax of a subprogram call is the same as in AmigaBASIC:
[CALL] sub-name[(parameter-list)]
The only difference is that the parentheses around the parameter list are
not optional when CALL is omitted -- unless there are NO parameters.
CALL *must* be used after THEN in a single-line IF..THEN statement.
By default, every subprogram has a return type of single-precision (just like
variables). The DEFxxx directives can be used to change the default data type
of subprograms, as can the trailing characters !#$&%. A subprogram name can
also be preceded by SHORTINT,LONGINT,ADDRESS,SINGLE or STRING as yet another
alternative to setting the subprogram's return type.
The fact that ACE subprograms can be easily used as functions pretty much
obviates the need for DEF FN. However for reasons of compatibility with
AmigaBASIC and other BASICs, as well as its utility for simple functions,
ACE supports DEF FN (as of v2.0).
A subprogram is given a value either inside the body of the relevant
subprogram or in the main program (eg: to zero it) - ala Pascal - thus:
sub-name = <expression>
However, subprograms cannot be assigned a value in any other way (eg: with
INPUT or READ).
A subprogram can be used in an expression, whereupon the subprogram is called
and its value pushed onto the stack for inclusion in the final result of the
expression, eg:
- page 16 -
x=n*pow(n)
where "pow" is a subprogram with one parameter.
While subprogram declarations can appear anywhere within the program text,
ACE requires that declarations precede calls. So:
sub test
print "hello"
end sub
test
is legal, but:
test
sub test
print "hello"
end sub
is not, and will yield an "undeclared subprogram" error. To get around this,
a forward declaration can be used:
declare sub test
test
sub test
print "hello"
end sub
Forward declarations can include a parameter list. If you later declare
the actual SUB with a different parameter list and you've already called
the subprogram after a forward declaration, the results will be unpredictable.
I may place tighter controls on this at some stage.
Actual parameters are checked for number and type against formals, and
parameter count mismatches result in a compilation error. An actual parameter
is coerced to the formal parameter's type.
ACE's parameter passing mechanism for subprograms is NOT the same as that
used for assembly code routines or external functions. In other words, the
standard C parameter passing mechanism is not used for SUBs. This may be
changed in the future as it makes object modules written in ACE incompatible
with C or assembler object modules in this respect.
Changes made to a formal parameter have no effect upon the actual parameter
in a simple call to an ACE subprogram, but see "Limitations" re: overwriting
of strings/arrays during recursive calls; see also "Structures" below.
The formal parameter list consists of identifiers separated by commas. Each
identifier may also be preceded by: SHORTINT,LONGINT,ADDRESS,SINGLE or STRING
to avoid the use of a qualifier (%&!$).
Actual parameters can basically be any type of expression. A whole array
cannot be passed as a value parameter in ACE however.
- page 17 -
There is an arbitrary upper limit of 40 parameters per subprogram at the
moment, which may be removed at some stage.
Main program variables and arrays can be accessed and modified within
subprograms via the SHARED directive. All shared variables are passed by
reference to a subprogram.
Multiple SHARED statements are allowed within a single subprogram.
DIM SHARED is not allowed. An array is declared to be shared in exactly the
same way as simple variables, for example:
DIM x(10)
sub thing
shared x
.
.
end sub
Note that parentheses are not required after an array in the shared statement,
nor are they legal in ACE.
The differences between variable parameters and shared variables in ACE
are that:
- Shared variables only allow access to main program
variables from a subprogram, and do not provide a
mechanism for changing the value of variables in
one subprogram from another.
- The name of a variable to be shared must correspond
to the name of an existing (ie: already referenced/declared)
main program variable.
Although variable parameters are not explictly provided by ACE there are
two ways to implement them: using indirection operators for simple variables
and the ADDRESS option of DIM and STRING (see also "Structures" section).
Here's an example of call-by-reference parameters for simple variables
(DEFxxx directives are used here for clarity):
deflng x '..x is an address holder
defsng n '..n is a single-precision variable
sub doub(x)
*!x := *!x * 2 '...n=n*2 [note the ":=" symbol!]
end sub
n=22.5
print n
doub(@n) '..pass the address of n
print n
which passes the single-precision variable n by reference to the subprogram
doub, where n is doubled. This will first print 22.5 and then 45.
For an array, the following could be done:
- page 18 -
deflng x '..x is an address holder
sub test(x)
dim a(10) address x '..points to n
a(3)=a(3)+12
end sub
dim n(10)
n(3)=2
print n(3)
test(@n)
print n(3)
which would print first 2 and then 14.
The same mechanism can be used to pass a string variable by reference.
These variable parameter mechanisms are most useful when used to pass data
*between* subprograms, otherwise it is simpler to use SHARED variables.
The following table shows the possibilities regarding parameters and shared
variables in ACE:
+---------------------+---------+-------------+------------------------------+
| Data Type / Object | Shared | Value param | Variable param |
+---------------------+---------+-------------+------------------------------+
| SHORTINT VARIABLE | YES | YES | YES - *%addr |
| | | | |
| LONGINT/ADDRESS VAR | YES | YES | YES - *&addr |
| | | | |
| SINGLE VARIABLE | YES | YES | YES - *!addr |
| | | | |
| STRING VARIABLE | YES | YES | YES - STRING x ADDRESS addr |
| | | | |
| EXTERNAL VARIABLE | NO | YES | YES - *%, *&, *!, STRING .. |
| | | | |
| ARRAY | YES | NO | YES - DIM x ADDRESS addr |
| | | | |
| STRUCTURE | YES | NO | YES - See "Structures" below |
+---------------------+---------+-------------+------------------------------+
Note: In the above table, "addr" is a long integer address. VARPTR or @
can be used to obtain this. The address is passed to the SUB by value.
Structures
----------
Structures have been included in ACE mainly because of their utility in
gaining access to operating system functions.
Legal structure members are of the following type: BYTE (in structures only),
SHORTINT, LONGINT, ADDRESS, SINGLE, STRING. The latter can have an optional
size specification.
If an array or structure is required as a structure member, it is necessary
to use STRING <ident> SIZE <bytes>. The address of the member can then be
found with @ or VARPTR and assigned to an array or structure variable.
If you want to have an address (ie: pointer to an object) as a structure
- page 19 -
member simply declare it as an item of type ADDRESS (or LONGINT).
When declaring a structure, the only difference between the following two
forms:
DECLARE STRUCT mystructtype mystruct
and
DECLARE STRUCT mystructtype *mystruct
is that for the former, an appropriate data object is created (on a long
word boundary), but not for the latter.
In both cases, mystruct contains the start address of a structure of type
mystructtype. In the second case, the address is NULL until assigned a value
(eg: with ALLOC). In both cases, the address can be reassigned at will,
although this should only really be done for structure pointers (the second
form).
Since both forms of structure declaration result in an address being stored
(in mystruct in the example), the dereferencing operator is always "->".
examples:
--------
PRINT mystruct - prints the start address of the structure.
PRINT mystruct->mins - prints the value of a member called mins.
The SIZEOF function can be used to determine the size of a structure type
if allocating memory for a structure (see linkedlist.b).
ACE structures are stand-alone data objects, and cannot be elements in an
array (although structure addresses can be).
ACE structures can be SHARED to allow its member's values to be modified,
or a structure's address can be passed to a subprogram, eg:
struct my
longint one
longint two
end struct
sub test(addr&)
declare struct my *second
second=addr&
second->one = second->one * 2
end sub
'..main
declare struct my first
first->one=12
print first->one
test(first)
print first->one
which will print 12 followed by 24.
The following code allocates enough memory to hold a structure of type "my"
gives values to the structure's 2 members, and changes the address held by
- page 20 -
the structure variable "third" to the start of the newly allocated memory
area:
const PUBLIC=2
declare struct my *third
sub create(ADDRESS a_struct)
declare struct my *temp
temp = Alloc(sizeof(my),PUBLIC)
temp->one = 16
temp->two = 10
*&a_struct := temp '..change structure variable's value
end sub
'..main
create(@third)
.
.
Finally, it is not currently possible to use INPUT, (LINE) INPUT# or READ
in conjunction with structures.
Shared library function calls
-----------------------------
ACE provides access to shared libraries in the same way as AmigaBASIC does
with the exception that you MUST declare a function in order to use it.
Also, the library in question must either be in LIBS: or in ROM.
As of version 2.0, ACE and AmigaBASIC are otherwise pretty much the same.
The ACE commands also retain their earlier syntax for backward compatibility
and convenience.
The commands are as follows:
LIBRARY <libname>
- Where <libname> is the name of a shared library with or
without quotes (eg: "graphics", "graphics.library", graphics).
- A ".library" or ".bmap" suffix is allowed but optional.
- The LIBRARY command opens the shared library and provides
a copy of its base address for use internally by function
calls.
- If a library can't be opened at run-time, the program
will abort.
LIBRARY CLOSE [<libname>]
- Closes the specified shared library or all open libraries
if no library name is given.
- Closing a library more than once will cause no harm.
- page 21 -
Notes about standard libraries used by ACE:
- There are currently six standard libraries which are often opened
by ACE routines during a program run. These are: dos, intuition,
graphics, mathffp, mathtrans and translator libraries.
- If one of these six is opened by the LIBRARY command it will
be opened at the start of the program *and* closed at the end.
Any other library will be opened and closed at the points in
the program specified by you.
- You don't actually have to close any of the six libraries
mentioned above, but it won't hurt.
- Moreover, you never have to open or close the dos.library
since ACE opens it for EVERY program.
DECLARE FUNCTION <funcname>[%&!#$][(param-list)] LIBRARY [<libname>]
- Where <funcname> is the case sensitive name of a function in a
shared library.
- <funcname> may have a trailing character (&%#!$) to indicate type,
otherwise default data type rules apply for the function's return
value. This character is optional when CALLing the function.
- The optional parameter-list is for documentation purposes only
and is otherwise ignored.
- If <libname> (same as for LIBRARY and LIBRARY CLOSE) is specified,
ACE only looks in the bmap file for that library, otherwise ACE
looks for the function in the bmap files for all open libraries
and all the standard libraries known to the compiler. Needless
to say that specifying <libname> results in faster bmap file
entry lookups. This option is not given by AmigaBASIC however.
- Example: DECLARE FUNCTION SetSoftStyle LIBRARY
[CALL] <funcname>[(parameter-list)]
- Transfers control to the function <funcname>, loading the
appropriate registers before doing so, according to the
information about that function in the library's bmap file.
- The return value of a function can be accessed by calling
a function as part of an expression, eg: addr& = AllocMem(100,2).
Function declarations are GLOBAL. They are are NOT optional in ACE.
** PLEASE NOTE ***
No type checking of parameters is performed, so expect weirdness if you pass
values of the wrong type. If the RKM or whatever reference you are using for
library functions says to use a long integer, then do so. Don't assume that
a short integer will be coerced to a long integer. It won't. ACE cannot
get that information from the bmap file in which a function is found and
parameters are not sign-extended by default.
- page 22 -
When passing strings as parameters it is not necessary to add a CHR$(0)
to the end of a string since ACE strings are already NULL terminated.
Either VARPTR or SADD can safely be used to find the address of a string
variable or constant. Actually, the use of SADD or VARPTR for strings
passed to library functions is optional, but it's probably a good idea to
use one or the other all the time, for consistency's sake. These comments
also apply to calling machine code routines and external functions.
It is up to YOU to open and close libraries correctly. ACE doesn't keep
track of this, and will try to jump to a library function so long as
it finds a reference to it in a bmap file even if the library hasn't
been opened! As mentioned above, it is not necessary to open and close
dos.library because _every_ ACE program does this.
ACE expects the bmap file for a library to be in the directory ACEbmaps:
(see "Installation"). I have been advised by Commodore Australia that
these files may be distributed with ACE but I will wait until I have this
in writing before providing them with the archive (I've waited several
months already).
As of version 2.0, I have provided a program (FD2BMAP) which is functionally
equivalent to ConvertFD (since this may NOT be freely redistributed) so that
bmap files for new libraries can be created. The source code for FD2BMAP is to
be found in the ACE:utils/fd2bmap directory and was written in ACE by Harald
Schneider, with some modifications by me.
The 1.3 FD files can be found in the BasicDemos drawer on the Extras disk.
The FD files for Release 2.x/3.0 are available from Commodore for about $30
(you get six disks of developer goodies: ask for the Native Developers Kit).
AmigaBASIC cannot handle functions which use address register a5. This is
not true for ACE. Neither ACE nor AmigaBASIC allow the use of functions which
use register a6.
See library.b for an example of how to use shared library functions in ACE.
Machine code calls
------------------
ACE supports AmigaBASIC's mechanism for calling machine code routines and
the passing of parameters to such routines. AmigaBASIC's stack conventions
are also followed (ie: C style parameter passing).
The syntax for calling such a routine is:
CALL long-integer-variable-name[(parameter-list)]
Note that CALL is NOT optional. Also, the variable containing the address
of the routine *must* be a long integer in ACE.
For example,
CALL caps&(length&,addr&)
will set up the stack like this:
8(sp) = addr&
- page 23 -
4(sp) = length&
0(sp) = return address
on entry to the machine code subroutine caps&.
On exit from a routine, ACE cleans up the stack by POPping all parameters.
You can use a short integer array, a string or an allocated area of memory
(with ACE's ALLOC function) to poke the bytes of a machine code routine into.
I prefer the latter method.
Note that because ACE treats ASCII 0 as the end-of-string character, don't
use the string-building method, eg:
z$=""
for i=1 to N
read b
z$=z$+chr$(b)
next
since if b=0, chr$(b) will be the NULL string. If you want to use a string,
do the following:
z$="" '..or STRING z$ SIZE 100 (if there are 100 bytes of MC).
addr&=sadd(z$)
for i&=0 to N-1
read b%
poke addr&+i&,b%
next
call addr&
The latter is okay, so long as you don't allocate other strings with odd
sizes. But if you want to be sure that you have an area of memory which
is long-word aligned, use ALLOC, eg:
addr&=Alloc(100,2) '..100 bytes of PUBLIC memory
for i&=0 to N-1
read b%
poke addr&+i&,b%
next
CALL addr&
The above examples assume the presence of appropriate DATA statements. See
the prgs/MC directory for working examples.
ACE also supports inline assembly code inclusion. See ASSEM..END ASSEM in
ref.doc for details.
External references
-------------------
Reference can be made to a variable or function in another file which is
resolved at link time. You may for instance, have written a function in C
or assembler. It is possible to pass parameters to, call and obtain return
values (as with ACE SUBs) from such a function in ACE after declaring an
external reference to the function with the EXTERNAL FUNCTION directive
(see command and function reference for syntax).
When passing parameters, standard C parameter passing conventions
- page 24 -
are used. Although some C compilers seem to pass all parameters as
4 bytes per parameter on the stack, ACE allows 2 (short words) or 4
byte parameters. Be aware of this! See prgs/ExternFunc for examples.
External variables can be assigned values like normal variables, eg:
external RangeSeed&
RangeSeed=5276&
All external reference identifiers have an underscore prefixed by ACE but
this is optional when declaring or using an external reference. C compilers
prepend an underscore to external symbols, so ACE does too.
Note that the names of external references ARE case sensitive.
Also, the bas script can take as a third argument the name of the object
file produced from the original C or assembly source (ie: .o or .lib file)
which contains the external function or variable to be linked with your
ACE program.
It's not easy to call ACE SUBs from C or assembler for two reasons:
1. ACE SUBs don't currently use C parameter passing conventions.
2. ACE produces whole assembly source programs, not just functions
like a C compiler does.
For now, you'll have to get your hands dirty with the assembly source code
produced by ACE if you want to do this. ACE's -c switch may be of some help
here.
Windows
-------
You can open up to nine user-defined windows on the Workbench screen.
See the command and function reference for the syntax of the WINDOW
statement.
All user-defined windows are now (as of ACE v2.0) Intuition windows with
each characteristic being configurable via the "type" parameter as per
AmigaBASIC (see ref.doc).
Windows can be opened on the Workbench screen or a user-defined screen.
The zeroth window (the shell/CLI, if the program was CLI launched) is now
the only instance of a DOS console window in ACE.
The WINDOW function takes a single parameter and returns information about
the current output window. See ref.doc for details.
Note that for user-defined windows, close-gadget clicks must be handled by
the use of ON WINDOW event trapping or via the "w" compiler option.
Screens
-------
You can open 9 screens at once (memory permitting!).
By default, when a screen is opened, a BORDERLESS window the same size
as the screen is also opened. Subsequent graphics and text commands send
- page 25 -
their output to the newly created screen's window (until the screen is
closed). Note that this facility is not provided by AmigaBASIC.
In addition, you can open user-defined windows onto any screen, in which
case all output is directed to the current output window.
Avoid mixing the use of default and user-defined windows. Except for the
simple case in which you use nothing but screens and their default windows
you should consider windows to be the primary output destination for graphics
and text.
When a screen is closed, ACE makes the screen with the next highest id
the current one, so it is advisable to open and close screens in ascending
and descending order.
A special SCREEN function exists in ACE which returns pointers to various
Intuition structures (window,screen,rastport,viewport). This is detailed in
the command and function reference (ref.doc) as are the following commands:
SCREEN, SCREEN CLOSE, PALETTE and PRINTS. The latter is now redundant since
all commands and functions can - as of v2.0 - be used transparently for
screens, user-defined windows and the shell/CLI.
Gadgets
-------
ACE supports the Amiga's three standard gadget types: boolean, proportional
(vertical and horizontal), and string (including long integer).
Since one of my aims is to support all Amigas running everything from Wb 1.3
to Wb 3.0, I have chosen to stick with simple Intuition gadgets for now.
In a future revision, a run-time test may detect machines running Wb 2.x/3.0
and use GadTools gadgets instead. Furthermore, other gadget types may then be
supported (radio buttons, check boxes etc).
Memory permitting, up to 255 gadgets can be created during a single program
run.
The GADGET command creates a gadget with specific features while GADGET CLOSE
removes the gadget from the window. Once created, a gadget can be enabled or
disabled, indeed it can be disabled upon creation if so desired.
Having created a gadget or gadgets, you must then decide how to receive and
handle information from them. ACE provides four methods: standard event
trapping (ON GADGET), polling (via the GADGET function), WAITing for a
specific gadget or WAITing for any gadget.
Where it is possible to make your programs modal (ie: focussed upon a single
event or event type) you should do so. The GADGET WAIT command allows this.
See "Event Trapping" for more about modality in ACE programs.
The following commands set up a window with two boolean gadgets and a close
gadget. The latter is set up by Intuition with the WINDOW command.
The program traps WINDOW and GADGET events. The comments should help you
understand the code.
CONST having_fun = -1&
- page 26 -
WINDOW 1,"Gadgets",(0,0)-(640,200),8
GADGET 1,1,"Hit Me",(3,3)-(75,20),1,1
GADGET 2,1,"Quit",(100,150)-(200,175),1,2
ON GADGET GOSUB gadget_handler
GADGET ON
ON WINDOW GOTO quit
WINDOW ON
'..main loop (actually does nothing, but is necessary for event trapping)
WHILE having_fun
'..have a nap while nothing's happening
'..(don't hog the machine by busy waiting)
SLEEP
WEND
gadget_handler:
'..find out which gadget was selected
gad = GADGET(1)
LOCATE 12,40:PRINT "<<";gad;">>"
if gad = 2 then quit
RETURN
quit:
GADGET CLOSE 2
GADGET CLOSE 1
WINDOW CLOSE 1
END
Alternatively, you could poll for a gadget to the exclusion of other events:
.
.
'..await a gadget selection
REPEAT
WHILE NOT GADGET(0)
SLEEP '..be a little nice to the operating system
WEND
'..which one?
gad = GADGET(1)
LOCATE 12,40
PRINT "<<";gad;">>"
UNTIL gad=2
.
.
Finally, you can wait for a gadget:
.
.
GADGET WAIT 2 '.."GADGET WAIT 0" waits for ANY gadget! BEST method!
.
.
See the program prgs/misc/ACEgadgets.b for an example of gadget programming
- page 27 -
in ACE.
For boolean gadgets you can - if you need to - get information about the
width and height of the gadget's text font by calling WINDOW(12) and
WINDOW(13). If you need more precise width information, use the graphics
library TextLength function.
ACE string gadgets currently only allocate enough text buffer space for
the number of characters which can fit into the gadget (as specified by
the bounding box in the GADGET command).
In the next revision, this will be changed to allow a larger buffer such as
InputBox[$] permits (see the "Requesters" section) either via an optional
buffer-size parameter or a fixed-size buffer. I have not yet decided upon
the best course to take on this matter.
For more details about the GADGET commands and function, see ref.doc.
Menus
-----
ACE supports menus ala AmigaBASIC, with two additions: menu item command
keys and a MENU WAIT command. The latter puts the program to sleep until a
menu event occurs. The former is specified by an optional parameter to the
MENU command, for example:
MENU 1,5,1,"Quit","Q"
defines menu item number 5 in menu number 1 to be the 'Quit' option and
sets up a command-key sequence (Amiga-Q) for that item. The third parameter
enables the menu item as per AmigaBASIC.
Note that although ACE adjusts menu text for font size and type as set
via preferences, some fonts may require you to pad your menu title/item
names with blanks when using command keys to avoid overlaps.
For an example of menu programming with ACE see prgs/ifs.b. For a better
example, see the source code for AIDE. Over time I will modify some of
the other example programs in the archive so that they are menu-driven.
See ref.doc for more details about the MENU commands and function.
Requesters
----------
As of version 2.0, ACE supports 3 standard requesters:
- System requester
- File requester
- Input requesters x 2
My recent experiences with Visual Basic for Windows have led me to add these
to ACE since I have now come to expect them. You can get the most commonly
needed requesters in a single line of ACE code!
If you are running Wb 2.x and above, ACE generates an ASL file requester.
For Wb 1.3 an ARP file requester is invoked for two simple reasons:
- page 28 -
- The arp.library is common on Wb 1.3 systems.
- The ARP file requester is still quite good.
See MSGBOX, FILEBOX, INPUTBOX and INPUTBOX$ in ref.doc for more.
Turtle Graphics
---------------
You may be wondering one or more of the following:
- what the heck is Turtle Graphics?
- isn't that for kids?
- why did he include THAT?
To answer the first question: Turtle Graphics (TG) originated as a subset of
the language LOGO invented by Seymour Papert et al at MIT. LOGO was originally
intended as a language for learning. Children are able to write simple programs
to draw shapes on the computer's screen or move a Turtle - a dome-shaped robot
- on a sheet of paper on the floor, learning about geometry "by doing" and
having fun to boot.
LOGO also has many Lisp-like qualities and so can be used as a serious
language for AI work, although to my knowledge, it's not.
But I digress. Apart from the fun kids can have with TG, it's actually
possible to construct quite complex shapes with it. Combined with recursion,
TG is a powerful tool. It is particularly useful in plotting many fractal
shapes (see snowflake.b, dragon.b).
Since the first LOGO, there have been many manifestations of TG. Turbo Pascal
for the PC and the Mac have both had TG.
A couple of years ago, I wrote a pure TG subset of LOGO which used the same
syntax as the original language and allowed recursive procedures. I've also
written TG functions in C. Both of these have been useful to me and I've often
wished that BASIC came with TG built-in. Well, now one dialect does!
For some examples of the use of Turtle Graphics in ACE, see the following
programs:
- tree.b
- flower.b
- boxit.b
- torus.b
- dragon.b
- snowflake.b
- bst.b
The above discussion should have answered the second question. As for
the third, the answer is: because I wanted to!! :^)
Okay, enough philosophy. Here's the ACE stuff:
BACK n - move turtle back by n.
FORWARD n - move turtle forward by n.
HEADING - return turtle's current heading in degrees (0..359).
HOME - move turtle back to its home position.
PENDOWN - put turtle's pen down.
PENUP - lift turtle's pen up.
- page 29 -
SETHEADING degs - change turtle's heading to degs.
SETXY x,y - change turtle's current x,y location.
TURN degs - rotate turtle by degs.
TURNLEFT degs - turn turtle left by degs.
TURNRIGHT degs - turn turtle right by degs.
XCOR - return turtle's current x-coordinate.
YCOR - return turtle's current y-coordinate.
where:
- n is pixels (x:y ratio is 2.25:1 -- assumes hi-res screen).
- degs is a signed short integer representing degrees with
the turtle starting at a 270 degree orientation -- pointing up).
- home is the turtle's x,y start location (0,0).
Note that the X:Y ratio can be modified thus:
EXTERNAL _tg_xy_ratio!
_tg_xy_ratio = 1.125 '..1.125 is about right for a lo-res screen
Most LOGO environments use a coordinate system where 0,0 is at the center of
the screen and positions to the left and down of this origin are negative
while those up and to the right are positive. ACE's TG system however, uses
the Amiga's normal graphics coordinate system with 0,0 at the top left of
the screen/window so as to maintain consistency with ACE's normal graphics
commands and functions (eg: POINT, PSET, LINE, PAINT, CIRCLE, AREAFILL).
If a negative value is specified for the turnleft or turnright commands,
the turtle will be rotated in the opposite direction to that indicated by
the the command name. Note that there is also a TURN command.
When using ACE's TG system, it's best to think of an imaginary turtle (in LOGO
it's usually a small triangle on the screen) which rotates and moves according
to your whim. The turtle can either have its pen lowered or raised - and will
therefore draw or not - which is useful when you need to move in a relative
fashion from one location to another without drawing anything.
SetXY is like the graphics library Move() command and may need to be preceded
by PENUP unless you want to draw a line.
To change the colour of the pen, use the COLOR command.
That's probably enough about Turtle Graphics. Oh, by the way, if you ever
get the chance to read Papert's "Mindstorms", do so. It's good value.
While I think of it, Sherry Turkle - that's Papert's wife - wrote a book
called "The Second Self: Computers and the human spirit", which I recommend
if you're at all interested in the psychological/social effects of computing.
Sound
-----
ACE provides you with similar functionality as AmigaBASIC for sound
generation. It also allows you to do some things that AmigaBASIC doesn't.
See sound.b for an example of how to use ACE's sound facilities in general.
How many times have you wished that AmigaBASIC would let you produce
white noise easily like the good ol' C64 and Vic-20 did?
- page 30 -
Well, you'll be pleased to know that ACE allows you to do this. All you
have to do is allocate about 4000 or more bytes of Chip RAM (upwards of
4000 bytes yields better quality white noise), poke it with random values,
(between -128 and 127) call WAVE and you're set (see sound.b)!
Moreover, you can actually play sound samples (IFF or otherwise) in the
same way, using just the two commands WAVE and SOUND. In fact, I've written
a program in ACE that plays sound samples, recognising the IFF 8SVX format
(see play.b).
As with AmigaBASIC, a sine waveform is the default, but through the WAVE
statement you can create any waveform you wish including sawtooth, triangle
and square.
WAVE has the following syntaxes:
WAVE voice,SIN
and
WAVE voice,waveform-address,byte-count
where waveform-address is the start of a block of memory where the waveform
resides (an area of ALLOC'd CHIP memory) and byte-count is the number of bytes
in the waveform table.
The SOUND statement syntax is as follows:
SOUND period,duration[,volume][,voice]
This is different to AmigaBASIC in a number of ways. First, in ACE you
specify the sampling period NOT the frequency. This was easier to implement
and still provides the same functionality, but if you want specific notes,
you'll have to do the calculations yourself (see equations below).
Sampling period is inversely proportional to frequency, so a high sampling
rate corresponds to a low frequency and vice-versa. ACE allows you to
specify a sampling period in the range 124..32767.
The duration is a single-precision value as in AmigaBASIC but can range from
0..999 (instead of 0..77). This range is somewhat arbitrary, but gives plenty
of scope for large sound samples. This specifies the length of time that a
tone should be played for. A duration of 18.2 corresponds to about 1 second.
Volume defaults to 64 if not specified and can range from 0..64.
The voice can be in the range 0..3 - since there are 4 audio channels -
with 0 & 3 corresponding to the left speaker and 1 & 2 to the right.
The default voice is 0.
At the moment, ACE's SOUND statement isn't very good when used to produce a
series of short pulses, although this is somewhat dependent upon the waveform
in use. In any case, more work needs to be done in this area to prevent
"popping" between SOUND statements when the audio hardware is turned on and
off in rapid sequence.
ACE sound is produced by programming the hardware directly. A future version
will probably utilise the audio device instead. Indeed, it's fair to expect
that SOUND in ACE will change in the future to be more in line with AmigaBASIC
- page 31 -
(see also "Future Versions").
Finally, here's some useful equations for use in conjunction with ACE's SOUND
statement:
samples/second to period:
------------------------
period = 3579546 / samples-per-second
musical note to period:
----------------------
period = 3579546 / (length * frequency)
where length is the size of the waveform table in bytes
(32 bytes for ACE's sine waveform) and frequency is the
note itself (eg: middle C is 523.25 Hz).
duration value for one waveform cycle:
-------------------------------------
duration = .279365 * period * length / 1E6 * 18.2
Event trapping
--------------
ACE provides for AmigaBASIC-style event trapping. The following event
types are supported:
BREAK - user break: ctrl-c.
MOUSE - left mouse button press.
TIMER(n) - cause a trap every n seconds.
ERROR - trap file and serial I/O errors.
MENU - menu selection.
WINDOW - window event: close-gadget click.
GADGET - user-defined gadget selection.
Event trapping in ACE works by checking for a given event at strategic points
in a program (before NEXT, WEND, GOTO, CALL, PRINT etc) and if an event is
detected, control is passed to a trap handling routine. Hence, trapping here
does not refer to CPU traps (exceptions).
Even if your program expects to do nothing but trap events, you'll need a
loop like this:
WHILE -1
SLEEP '..don't hog CPU time (intuiticks will be reported though)
WEND
if you wish to have any events handled by your program.
The specification for the trapping of an event is:
ON <event> GOSUB | GOTO <label>|<line-number> (eg: ON BREAK GOTO quit)
which indicates the routine to which control is to be passed when an event
is trapped. This is followed at some stage by:
<event> ON (eg: BREAK ON)
which causes the compiled program from that point UNTIL the trap handling
- page 32 -
routine, to contain event trapping code.
It is a good idea to put trap handlers at the end of a program, since once the
handler for an event is found by the compiler, no more event trapping code is
generated for that event even if there is code below the handing routine. In
other words, the equivalent of an <event> OFF (see below) command is issued
once the trap handling code is found by the compiler.
Other commands are:
<event> STOP
which disables trapping for the event until another <event> ON is issued,
and:
<event> OFF
which disables trapping for the event permanently.
Just so there is no misunderstanding, the latter two commands prevent the
inclusion of event trapping code for a specific event in your program at the
assembly source level. They do this from the point in an ACE program at
which they are issued.
Here's a typical example:
ON BREAK GOTO quit
BREAK ON
for i=1 to 1000000
print i
next
quit:
PRINT "**break!"
STOP
Please be aware that mixing some event trapping types can have unexpected
results. More specifically, it is best to trap only one kind of event where
possible. Also, sitting in a tight loop awaiting a keypress via INKEY$ at
the same time as trying to trap GADGET events will not work very well.
During MENU/GADGET event trapping, WINDOW close gadget events are passed
onto ACE's WINDOW event trapping mechanism due to the desirability of having
both enabled at once. This also holds true for MENU/GADGET WAITing which is
a far better mechanism to use wherever possible. See "Gadgets" and "Menus"
section above.
Where possible, make your program modal (ie: focussed on one event at a time).
You'll have to experiment to find out what works well together and what
doesn't.
Don't forget though, that you can quite easily have N different kinds of
event trapping in one program, and use <event> ON|OFF|STOP to control which
events are currently being paid attention to and which are not.
Error Handling
- page 33 -
--------------
The compiler messages generated by ACE are often different to the ones in the
AmigaBASIC interpreter (and ACE doesn't beep at you with each error) but they
are usually fairly clear.
Syntactically incorrect programs can lead ACE to produce a bunch of spurious
error messages. In such cases, it's best to ignore all but the first one or
two, unless there are "clusters" of messages which are separated by periods
of error-free compilation.
If you leave out END IF, WEND, UNTIL, NEXT, END SUB, END STRUCT or END CASE,
there will be a corresponding number of error messages at the end of the
compile. If you leave off two WENDs, you'll get 2 "WHILE without WEND" error
messages.
ACE generally reports the first error in a line of code and ignores the rest
of the "bad" line. A typical message consists of the line containing the error,
a carat ("^") marker, and the error message itself. More work still needs to
be done on ACE's compile-time error handling, but it's bearable.
No error messages are issued by ACE programs at run-time. Generally, when a
program runs into something it can't do, or an erroneous request - like
trying to open two files to the same file number or trying to open a library
that doesn't exist - the program will either quit or just not have the desired
effect.
Note that while the ERR function and ON ERROR event trapping are supported,
only file and serial I/O errors are currently reported via these mechanisms.
Notes for assembly programmers
------------------------------
I've tried to make the assembly source files that ACE produces as
readable as possible by using meaningful data object names. See also
"Compiler options" re: the compiler's "c" switch.
Linked library routines use data registers d0-d6 and address registers
a0-a3, while d7 is used for array index calculations. Also, a4 and a5 are
used as stack frame pointers for variables and parameters.
Most db.lib routines don't save and restore registers via the stack, but
the use of registers is internally consistent (ie: all registers are up for
grabs but interdependent routines are written in such a way so as not to
conflict).
The use of linked libraries means that the size of all executables is
fairly large. But given that disk space and memory are cheap, I'd rather
this than the alternative of having every executable be dependent upon one
or more special shared libraries at run-time. However, I will try to reduce
the size of executables.
Due to BASIC's tendancy to coerce data types so much for the programmer,
the resulting code can look a little nasty, and big increases in efficiency
can be gained by careful combinations of data types in expressions.
Writing ACE has so far been a learning experience for me and if when I started,
I knew what I know now, I would have done many things differently.
My original rationale for passing parameters via registers to ACE (and shared)
- page 34 -
library functions was to improve execution speed. However, since I call lots
of other functions (eg: in ami.lib) which require their parameters to be on
the stack, I would probably call ALL functions in this way if I did it again.
Oh well.
Moreover, my desire for internal consistency led me to a rather odd method of
passing parameters to SUBs. This allowed me to treat parameters in the same way
as variables which is all very nice, but it led to other problems, chief among
them being the need to use a Forbid()/Permit() pair when sending parameters to
a SUB. This works fine however, so I'm taking the view that if it 'aint broke,
I probably shouldn't fix it.
Limitations
-----------
Variables do NOT get a default zero or NULL value, so don't assume ANYTHING
about the contents of an uninitialised variable. For example: PRINT X will
yield garbage if X has not been given a value. The optional variable
declarations provided in ACE are therefore worth using since they DO give
variables an initial zero or NULL value.
The precision of exponentiation begins to falter with large numbers because
all exponentiation is currently done with the single-precision math library
function SPPow().
While strings can be defined to be longer (or shorter) than 1K, there
are some ACE commands and functions which still assume a 1K limit, namely:
STRING$, SPACE$, LINE INPUT#, INPUT and SWAP.
Strings and arrays which are local to a subprogram will be overwritten if
the SUB has recursive calls to itself. The same applies to string parameters.
In all these cases a single static data item is being referenced.
If you issue RETURN from within a FOR loop, the return address will *not* be
the top item on the stack. Instead, FOR..NEXT loop data will be. A GURU will
almost certainly result. It is probably better to use a while or repeat loop
if you must RETURN from within a loop. See also EXIT FOR in ref.doc which
allows for the safe, early termination of FOR loops.
A shared variable cannot be used as a FOR loop index in ACE. Any attempt to
do so will result in a compilation error.
IF..THEN NEXT will not have the desired effect (it's bad coding anyway).
NEXT must always appear on a line by itself or as part of a multi-statement.
Expressions and parameter lists cannot currently be split over more than
one line.
The compiler only responds to ctrl-c during the main compilation phase,
and not during optimisation or when target code is being written.
Don't mix the compiler's "b" option with BREAK event trapping, as they will
conflict.
Known Bugs
----------
The SAY(n) *function* works under release 2.x but not under 1.3. Since the
function uses no 2.x-specific code, this is puzzling. The SAY command works
- page 35 -
under both 1.3 and 2.x however.
A68K sometimes complains about string literal definitions produced by ACE
if they are much longer than a single line.
Some fonts cause the INPUTBOX[$] display (string gadget) to be corrupted.
Stick to topaz for this where possible. A future revision will use a
GadTools requester (for 2.x machines).
Future versions
---------------
Random files and double-precision floating-point math are high on the agenda.
More graphics (eg: GET,PUT) commands and functions are planned and I also
intend to fix any remaining differences between ACE and AmigaBASIC in this
area.
Commands for IFF sound and picture file playing/viewing are likely.
I may provide support for sprites at some stage.
AGA screen modes are likely to be supported sometime.
Thus far, I've taken the approach of implementing what I most often use
and what I have often wished for in BASIC.
In recent times I have been impressed by three things in the programming
world: the rise of Object-Oriented Programming (OOP), Resources (as found
on the Macintosh and Windows) and Visual BASIC for Windows.
The idea of resources (pioneered by the Macintosh resource fork and
ResEdit) is a powerful one and Microsoft and Borland have picked up
on this in Windows. I wish the Amiga had them as standard, but alas it
doesn't. Still, the emergence of GUI building tools in the Amiga world
has gone some way toward making up for this.
I am trying to add more "visual" stuff to ACE, hence: gadgets, menus,
requesters, AIDE. You can expect to see more "visualising" of ACE as time
goes by, including a GUI designer written in ACE for ACE programs. To make
ACE like Visual BASIC would take a major rewrite, but I can at least try to
take advantage of some of its features and concepts.
I have no firm plans to include OOP capabilities in ACE, but one never knows.
There is some possibility of ACE taking on board some of the list-processing
features of certain functional programming languages (eg: Lisp, LOGO).
A note to PD libraries and reviewers
------------------------------------
I'd appreciate it if you would check with me (if possible) to ensure you
have the latest version of ACE before including it in your library or
reviewing it for a magazine. If you don't have e-mail access, I don't
expect you to do this (snail-mail is a pain isn't it!?).
Also, to those magazines who have reviewed ACE so far, let me say a big
THANK YOU. Good publicity is always appreciated as is acknowledgement of
the time I've spent on ACE. I hope I accept criticism with as much grace.
- page 36 -
Lastly, the wider ACE travels the happier I am, so I'm pleased to see
ACE turning up in the odd PD library. Please note however that I don't
wish people to profit financially from the distribution of ACE. You may
charge a fee which covers the cost of the disk and the copying thereof,
but no more.
Disclaimer
----------
Although every care has been taken in the development and testing of the
compiler and its libraries, the author will not be liable for damages caused
either directly or indirectly as a result of the use of ACE.
References
----------
The following references have been used throughout the development of ACE:
+----------------------------------------------------------------------------+
| "Amiga BASIC" (manual), 1985, Commodore-Amiga Inc. and Microsoft Inc. |
| |
| "Amiga ROM Kernel Reference Manual: Libraries and Devices", Commodore-Amiga|
| |
| Anderson & Thompson, 1990, "Mapping the Amiga", COMPUTE! Publications Inc.|
| |
| Bleek, Jenrich & Schulz, 1989, "Amiga C for Advanced Programmers", Abacus |
| |
| Bleek, Jenrich & Schulz, 1989, "Advanced System Programmers Guide", Abacus|
| |
| Choi, 1990, "Advanced Programming Techniques", University of Tasmania |
| |
| Commander, 1987, "Amiga Assembly Language Programming", TAB Books Inc. |
| |
| Dittrich, 1989, "Amiga Machine Language", Abacus |
| |
| Schaun, 1990, "Amiga C for Beginners", Abacus |
+----------------------------------------------------------------------------+
The most frequently used of these has been "Mapping the Amiga". Despite
its not infrequent errors, it remains an excellent resource. I have
also often referenced the Advanced Amiga C programming book by Bleek et al.
Although not listed, Commodore's Autodocs for the Amiga (supplied with the
Native Developer Kit) are also constantly used.
Young Choi's Advanced Programming notes in many ways provided the impetus
for the development of ACE. They were used in a compiler construction course
I took as an undergraduate. I thank Young for introducing me to the joys
of compiler writing, although I know he didn't intend me to spend *all* my
time writing compilers. :-)
Including the Pascal Minus compiler I wrote for that course, and the BASIC
interpreter I wrote during the same period, I've since written a version of
Logo (turtle graphics subset), a Forth interpreter and ACE.
Final word
----------
- page 37 -
Let me offer my thanks to Charlie Gibbs for his reliable assembler and to
the Software Distillery for Blink. Without these excellent programs, far
fewer compilers would have seen the light of day, including ACE.
Sozobon C (ZC) has always been a reliable workhorse for me, so a vote of
thanks goes to Sozobon as well. Isn't PD great?
I'd like to thank those people who have tested ACE so far: Addison Laurent,
Byron Montgomerie, Juergen Weinelt, Alan Peyton-Smith, Michael Zielinski,
Ken Thompson, Peter Zielinski and others.
These guys have given ACE a good workout on a variety of platforms ranging
from an A1000 running Wb 1.3 to 68030 machines running Wb 3.0. They have
all pointed out a number of bugs/problems to me which have either been fixed
or put on the list of things to fix.
I'd especially like to thank Michael Zielinski for discovering a particularly
serious bug which prevented branches of greater than 32K in length prior
to v1.02. Also, Enforcer hits reported by him started me on a trail which led
to a nasty string-related bug (see entry for 13/4/93 in docs/history).
Jarto 'Robin' Tarp pointed out how inefficient my first implementation
of INSTR was. It's considerably faster now (v1.02).
Let me stress that any remaining bugs in ACE are entirely my fault.
Many others have given me a great deal of encouragement and made useful
comments throughout 1993. I hope this continues.
I'd also like to thank my wife Karen, for her encouragement and support
and for putting up with the number of hours I spend at the computer. IFS
is her favourite program (see the "Green Fern") so it's dedicated to her.
Despite ACE's problems (see "Limitations" and "Known Bugs") it is already
proving to be a useful tool for me, and if others can derive benefit from it,
well, that's great. ACE is gradually coming to support the major features I
want it to, but there's still plenty of work to do.
The provision of calls to shared library functions, external functions,
machine code routines, inline assembly code, and include files should
go a long way towards making up for ACE's intrinsic shortcomings.
I hope you enjoy ACE and find it useful. I'm learning a great deal by
developing it and having a lot of fun in the process, so I'm not after
monetary rewards. Of course, please feel free to donate if you wish!
What I DO want is feedback. If you have any problems, requests, queries or
suggestions, I want to hear from you and I'd like to hear about interesting
programs you've written with ACE (send me the source if you like!).
Obviously I want to know about bugs too.
Remember that ACE is FreeWare, so redirect flames to /dev/null (ie: be nice).
Happy ACEing!
Regards, David Benn
19th January 1994
- page 38 -
+-----------------------------------------------+
| Internet: dbenn@leven.appcomp.utas.edu.au |
| |
| Compuserve: 100033,605 |
| |
| Discovery 40: 032432850 |
| |
| Phone: (003) 317 680 [home] |
| (003) 243 285 [work] |
| |
| Address: 181 St John Street, Launceston, |
| Tasmania, Australia, 7250 |
+-----------------------------------------------+
- page 39 -